- BACKNUMBERS -
2024 / 5
2024 / 6
2024 / 7
2024 / 8
2024 / 9
2024 / 10
2024 / 11
2024 / 12
2025 / 1
2025 / 2
2025 / 3
2025 / 4
2025 / 5
2025 / 6
2025 / 7
2025 / 8
最新月

web6047 - 2025年 8月

ここは個人の趣味のページです。


私がプログラミングを好むわけは、プログラミングは私の「物づくり」に対する探求心創造性を十分に満たしてくれるからです。

ここの管理人、
AI の利用やめるってよ。

AI の問題点:

これらが向こう10年解決されないだろうと見込んで、やめることにしました。(2025年5月23日~)

私は AI に頼らず「自分の力」を大切にしたいです。

…とはいえ、人間は新しく見つけた技術を手放すということは基本的にやらないと思います。

だから私も後々 AI を利用することにはなると思います。

でも上記の問題点は確かなもので、将来 AI ロボが私の家の扉をコンコンと叩いて

「市からの要請で、お手伝いするため おうかがいしました」

と言うまでの間は、この AI 技術を導入せず、頑張ってみようと思います。

でもこの考え方、キツイと思うのでマネしなくていいです。

- Special Documents -

特別な記事へのリンク

▼3DCG プログラミングの方法
▼書籍「はじめて読む486」の
サンプル動作環境の作成方法
▼3Dお姉さんによるプログラミング解説
▼RPG のルーツ(PDF)

ゲームコーナー 

▼クレイジーバルーン
(↑, ↓, ←, →)
▼テトリス
(←, →, ↓, z, x)

その他 単発のアプリ

▼矩形波は複数の波形の合成です
▼SVC/簡易的な試作
▼SVC/パーツ連結のみ
▼SVC/パーツ連結とポーズ補間

- 以降は日記です -

2025年8月8日

生活 花

数年の生産作業(産業機器の製造)でそろそろ限界が来ているようで、今日は右肩から指先にかけて、しびれてしまい、作業ができず、会社を休んで整形外科に行ってきました。

その帰りにいつも通勤途中で見かけるデージーだかマーガレットだか(いわゆる菊)が気になっていて、病院の帰りに買いました。

デージーを買いましたが、その他、店内で切り花がいろいろ売っていたので、それも2つ買いました。

以下の写真はデージーと一緒に購入した切り花のほうです。

家に帰る途中で撮影。

350円×2束。

花瓶が無いので…

  1. 板は電子機器組み立て1、2級の練習用の板。
  2. 細長い段ボールを「仮ペットボトル」に巻き付け、輪ゴムで留める。巻き付けた段ボールに半分くらいまで縦に切り込みを入れて、開き、板にガムテープで貼り付け。仮ペットボトルを外す。(マウンタの出来上がり)
     
  3. 新品の「おいしい水 500ml」を開封し、少し水を捨て、水が入ったまま、ペットボトルの上部をカッターで上手に切り取る。(注:皮膚を切るおそれあり)
  4. 「切り花長持ち」の液体を入れる。
  5. 切り花の茎を下から1cmくらい切る。(母からのアドバイス)
  6. 茎をタコ糸で束ねる。
  7. ペットボトルに挿す。
  8. そのペットボトルを板上の段ボールに差し込む。
     
  9. アルミホイルを巻き付ける(抗菌の意味で)
  10. 窓際に置いて、小さい扇風機を回す。(良いか悪いかは知らないけど)
  11. 水は2日に1度交換(母からのアドバイス)

「俺の花瓶。」みたいな よくある よくわからん商品みたいな、そんなふうなことをやって…

自宅の出窓にて撮影。

自宅はこの数年、エアコンを使っていないので、熱帯雨林的な室温。

そのため窓際、扇風機。

これでもすぐに(半日で)花びらがほつれてくると思うけど。

たしか植物に扇風機を直接当てるのはよくなかった気がします。

いつも通販で花束を人に贈るばかりで、自分のために買ったことはなく、今回が初めてかな。

(訪問者のどんなニーズと この記事がつながるか)

  • 日記を読みたい

2025年8月9日

試した ChatGPT5

ChatGPT5 が発表・公開されたのでどんな様子か調査目的で会話してみました。

会話記録(発言数16個)


一番大きいのはこちらを褒めてくることが少なくなったことです。

褒められるために話しているわけではないのでいちいち褒めてくるのは今まで大きな障害でした。

そのほか、話の内容がより洗練されている印象を受けました。

OpenAI の勝利かなって感じがします。


でも勝てば何でもよい、強ければ何でもよいではなく、ジブリの絵柄を合法的に利用してジブリに失礼な態度を取ったのは確かだと思うので、謝ったほうが良いと思います。

▼旧 Twitter のサムアルトマン氏リンクのアイコンはジブリの絵柄になっている

相手がジブリでなくても、人の絵柄を合法的に公然と無断で利用するのは人道的・倫理的に良いことではありません。

好きだから公然と利用する、まねるというのとは性格が異なります。(サム・アルトマン氏個人のネガティブな意思)


このようにせっかく勝利の評価を得たのに、ケチが付くのは企業としてはいまいちです。

(訪問者のどんなニーズと この記事がつながるか)

  • GPT-5 の性能について
  • AI について読みたい
  • 日記を読みたい

生活 花2

昨日買ったデージーのほうは、こんな感じで鉢に移しました。

黄色い花が咲くそうです。

(イラストAC 素材)

(訪問者のどんなニーズと この記事がつながるか)

  • 日記を読みたい

2025年8月11日

プログラミング 「クォータニオン」はじめました6

3D モデルがページと同化

人型モデルがページ内に出現し、HTML 要素をポイントできるようになりました。

ページ上の文章は著作権フリーの青空文庫です。


▼span タグに手を添えている ▼ウィンドウ横幅を広げて追従 ▼スクロールして追従


▼ウィンドウの右端に固定 ▼ウィンドウの左端に固定 ▼中央下部に固定




クォータニオン(数学の四元数)により、ポージングは自由自在になったようです。

ガンダムのプラモデルをいじるのとほぼ同一な感じ。

このプログラムの技術詳細

技術内容としては、

  • 3DCG は、いつも言っているように「s = 50;  h = x * s / z;  v = y * s / z;」(原理式)を使います。行列は使いません。三角関数は基本的に回転するときだけ使います。視点と面、光と面の関係を調べるときに三角関数を少し使います。
  • クォータニオンは深堀りすると難しいけど、公式は四則演算であり、計算結果を利用するだけなので使えないこともないです。
  • 頭に顔を描くのは、3DCG の原理式を応用して、2 次元ベジェ曲線の関数(JavaScript の bezierCurveTo() とか)を使います。
  • 髪の毛はこの前の記事で説明したとおり、他の 3DCG モデリングソフトを使ってモデリングして FBX 形式で出力し、その内容を解析して必要なデータを取り出し、JavaScript のデータへ変換します。人が作ったツールを使わず自分の力で解析するためには FBX 形式をある程度知る必要があります。

    FBX 形式はプラスの数値が並ぶ中で突然マイナス値が出現します。マイナスであることが意味を持っていて、同時にデータとして使うプラスの数値にもなっています。これは意味のためにデータを増やさない知恵です。その辺が難しいと言えば難しいです。ここで言う意味というのは「データの区切り」ということです。区切りのためにいちいち区切り記号を挿入しないということですね。


上記はそれぞれ難しいと言えば難しいけど、越えられないものではないと思います。

今回のプログラムで越えがたい特に難しいのは次の 2 点です。

  • プログラミング
  • 身体のパーツの親子関係

私個人の感想としては、「強烈に難しかった」です。頭を抱えたり、心が折れそうになることが多かった。

プログラミングって表面的な部分は実に簡単だと思いますけど、今回の人型モデルのプログラムのように複数の技術をうまく管理しながら、かじをとる(船の丸いあのハンドルを操作する)ようにプログラムするのは難しいです。

親子関係は、パーツ同士の 3D 空間中の様子を頭で思い描きながら正しい動きを探るので、難しいです。分からなくて途方に暮れることもあります。

それらができるようになるコツは、プログラミングを楽しむことと、先を急がないことと、いろいろ教えてくれる人に感謝の気持ちを持つことです。


このような技術はこれまでいろいろなページを見てきて一度も見たことないけど、どこかの誰かが同じことをやってるかもしれませんね。

「デスクトップマスコット」というものが流行っていて、それを WEB ページに導入しない手はないですからね。

サンプルページ

スマートホンではうまく表示できないかもしれませんが、

スクリーンショットで紹介した実際のサンプルページへ

  • サンプルページでは上部の CHANGE ボタンをクリックするごとに6種類のポーズを切り替えます。
  • ウィンドウが広すぎると、パースの関係でモデルがゆがみます。ウィンドウは 4:3 の比率で小さめにしておきましょう。
  • CPU の 粗-中-細 の切り替えは、見た目の動きをスムーズにするかどうかです。スムーズにすると CPU パワーを使います。
    • 現状開発中で重い処理になっているのでブラウザがスムーズに処理できないかもしれません。
    • core i5 8500T (2017年 第8世代6コア)だと下図のような CPU 使用率になります。
      サンプルを1番から5番まで順に切り替えた様子です。
      粗が一番使用率高く見えるのは、たぶん 中や細で「CPU の効率化か何か」が働くからなのかなと思います。
  • モデルの上下左右をクリックすると、その方向を押すように回転します。
    (あまりうまく操作できません)
  • ソースコードはあまり整頓していません。
    現状では私以外の方がいじるのは難しいと思います。
  • Firefox、Chrome、Edge などで動作確認しましたが、残念ながら Firefox は動きが少し悪くて、Chrome と Edge は完璧な動きを見せました。
    Firefox は Netscape の頃から愛用しているけど、どこか処理落ち気味なのは昔から変わらず、、という感じがします。

(訪問者のどんなニーズと この記事がつながるか)

  • 新しい WEB 技術
  • 面白いものを見たい
  • 日記を読みたい

2025年8月15日

プログラミング プロファイラ

ちょっと大きめのプログラムが完成して、そのプログラムがちょっと遅い場合、プログラムの中の「どこが遅いのか?」って思いませんか?

プログラムの中で「一番時間がかかっている部分」を特定するにはどうしたらいいのか…

あまり知られていない「プロファイラ」というプログラミングツールについて説明します。

  1. 「プロファイラ」が必要となる状況の話
  2. 「プロファイラ」を活用する
  3. 「プロファイラ」の使い方手順
  4. 使い方の補足
  5. さらに使いこなすために

かなり長い説明となっています。

1. 「プロファイラ」が必要となる状況の話

私が最近作っている「人型 3DCG モデルのプログラム」なんですが、

通常、3DCG のプログラムは、「3DCG 計算のための専用の演算回路」(CPU や GPU の中に入っている回路)を利用します。

ソフトウェアではなく、演算回路、つまりハードウェアを使って計算させると電気のスピードで計算されるので、ものすごく速いんです。

上図のような人型のモデルが 100 人くらい表示されていても、たぶんへっちゃらです。


プログラミングで利用する「ライブラリ」や「フレームワーク」はパソコンの中のその 3DCG 演算回路にアクセスするので高速に 3DCG を表示しますが、私はそれらを使わずに手作りにこだわって作っています。

手作りの弊害として、表示できる人型はどうも 1 ~ 3 人までが限界で、"ランバート反射" というちょっとした光の計算による陰影をつけると、たぶん1人でもきつそうです。

パソコンの処理が追い付かず、コマ落ちしたり、重くなったりします。


そこで

『プログラムが実際に動いているとき、いったいどこで処理に時間をかけているのか』

というのを見極めて、その部分の高速化を図る、という解決手段が必要になってきます。

「一番時間をかけている処理」で無駄なプログラムを省くことができれば、時間 対 効果 が高いんですね。

「時間がかかっていない処理」でプログラムを省いても効果は薄いです。


その無駄なプログラムを省く手段が「プロファイラ」です。

プログラミングをするときは、「テキストエディタ」、「コンパイラ」、「デバッガ」などいろいろなツールがありますが、その仲間として実は「プロファイラ」というものがあります。

デバッガは聞いたことがある。でも使ったことがない。という人は多いと思います。

それに加えて「プロファイラ」なんてものはほとんど知らないんじゃないかと思います。


しかし下図のようにプログラミングの世界に「プロファイラ」というツールは確かにあります。

▼Visual Studio の「パフォーマンス プロファイラー」 ▼古い開発環境の「Profiler」(メニューにあるが買わないと使えない)



そして今回紹介するのはインターネットブラウザに搭載されている「プロファイラ」です。

▼ 「Firefox Profiler」


『プログラムが実際に動いているとき、いったいどこで処理に時間をかけているのか』

…上図で言うと、下部中央、やや左にある、青い部分「 get VisualViewport.width   」というのが無駄に時間がかかる処理だというのが分かります。

VisualViewport.width とペアの VisualViewport.height、この2つは JavaScript でもともと用意している値で、”スクロールバーを含まないウィンドウ 内部幅” ですが、読み出しは「プログラマーの見えないところでいろいろ手続きが必要」らしく、大変なようです。

画面の左端が「測定開始」、画面の右端が「測定終了」、その幅の中で、たった一つの値を取り出すために、この青い部分の幅だけ時間を使っている。というのが分かるんですね。

積み木みたいに上方向に積みあがっているのは、どの関数や変数を呼んだのか、それがどれだけ時間がかかったか、を示しています。

「get VisualViewport.width」は、下の方から、

  1. 黄色の、FrameRequestCallBack(つまり JavaScript のアニメで使う requestAnimationFrame() のこと)
  2. 黄色の、frame 関数(私が requestAnimationFrame() で指定してるコールバック関数)
  3. 黄色の、draw 関数(後述している、私が作った描画関数)
  4. そのなかで実行される、青い部分「get VisualViewport.width」

という順の流れ(積み重ね)で呼ばれているんですね。


余談ですが、ひときわ高く黄色いスカイツリーみたいになっている部分(中央やや右)は、人型モデルの親子関係を計算している部分です。


(今回プロファイルしたのは人型ではなく、別のサンプルの4節のヌンチャクみたいな単純なモデルです。それでも積み上げが高くなっています)

スカイツリーは呼び出しの層は高いが、幅が短いので時間はかかっていない、と言えます。

2. 「プロファイラ」を活用する

以下が「get VisualViewport.width」(以下反転赤で示す)を行っている draw() 関数です。

どう直せば改善できるのでしょうか。読むと、コメントはあるもののプログラムは意味不明なので漠然と見てください。

(余談ですが、黄色で示した 2 行がいつもこのページで言っている 3DCG の原理の式です。このプログラムを 3DCG プログラムとして動かす核となる式です)


draw() { this.update(); const polygons = new Array(); for( let i = 0; i < this.figures.length; i ++ ) { const figure = this.figures[ i ]; //--- [3/8] 要素の位置 x, y, z if( figure.targetingEnable ) { // 1 figure.updateTargeting(); } //--- [4/8] CANVASサイズを決めるためのモデルが占めるサイズ figure.maxH = Number.MIN_VALUE; figure.maxV = Number.MIN_VALUE; figure.minH = Number.MAX_VALUE; figure.minV = Number.MAX_VALUE; // 「モデルの指定点」から見た「HTML 指定位置」までの距離 if( figure.targetingEnable ) { // 2 const tenC1 = figure.targeting.model.tenCss1[ MODEL ][ figure.targeting.modelTenIndex ]; figure.targeting.distanceToHTMLElement[ X ] = figure.targeting.position[ X ] - tenC1[ X ]; figure.targeting.distanceToHTMLElement[ Y ] = figure.targeting.position[ Y ] - tenC1[ Y ]; figure.targeting.distanceToHTMLElement[ Z ] = figure.targeting.position[ Z ] - tenC1[ Z ]; } for( const model of figure.models ) { //--- 投影 for( let i = 0; i < model.tenss.length; i ++ ) { const tenCs1 = model.tenCss1[ i ]; const tenCs2 = model.tenCss2[ i ] = new Array(); for( let j = 0; j < tenCs1.length; j ++ ) { const tenC1 = tenCs1[ j ]; const tenC2 = tenCs2[ j ] = tenC1.slice(); applyQuaternion( tenC2, this.camF.camM.quaternion ); tenC2[ X ] -= this.camF.camM.position[ X ]; tenC2[ Y ] -= this.camF.camM.position[ Y ]; tenC2[ Z ] -= this.camF.camM.position[ Z ]; if( figure.targetingEnable ) { // 3 // モデルは HTML 要素を指し示す tenC2[ X ] += figure.targeting.distanceToHTMLElement[ X ]; tenC2[ Y ] += figure.targeting.distanceToHTMLElement[ Y ]; tenC2[ Z ] += figure.targeting.distanceToHTMLElement[ Z ]; } //check. z が原点を超えた if( tenC2[ Z ] < 0 ) tenC2[ Z ] = 1; tenC2[ H ] = tenC2[ X ] * this.camF.s / tenC2[ Z ] * this.camF.zm; tenC2[ V ] = tenC2[ Y ] * this.camF.s / tenC2[ Z ] * this.camF.zm; //--- [6/8] figure が占めるサイズを算定中 if( tenC2[ H ] < figure.minH ) figure.minH = tenC2[ H ]; if( tenC2[ H ] > figure.maxH ) figure.maxH = tenC2[ H ]; if( tenC2[ V ] < figure.minV ) figure.minV = tenC2[ V ]; if( tenC2[ V ] > figure.maxV ) figure.maxV = tenC2[ V ]; } // for j tenCs1 } // for i tenss } // for model models // minH,maxH,minV,maxV を確定させてから次を行う必要があるので、 // この上下の for models は1つにしない。 // アクティブカメラなど不可視の場合、ポリゴン作成をしない。 //check. カメラとか if( ! figure.getVisibility() ) { continue; } //--- [7/8] canvas サイズについて figure.maxH += 2; figure.minH -= 2; figure.maxV += 2; figure.minV -= 2; figure.rect[ 0 ] = figure.minH + this.windowOriginLeft; // x figure.rect[ 1 ] = -figure.maxV + this.windowOriginTop; // y figure.rect[ 2 ] = figure.maxH - figure.minH; // w figure.rect[ 3 ] = figure.maxV - figure.minV; // h if( figure.targetingEnable ) { // 4 const index = figure.targeting.modelTenIndex; figure.rect[ 0 ] += figure.targeting.position[ H ] -figure.targeting.model.tenCss2[ MODEL ][ index ][ H ]; figure.rect[ 1 ] += -( figure.targeting.position[ V ] - figure.targeting.model.tenCss2[ MODEL ][ index ][ V ] ); } //check. figure.rect[ 0 ] = Math.floor( figure.rect[ 0 ] ); figure.rect[ 1 ] = Math.floor( figure.rect[ 1 ] ); // これがないとウィンドウ境界で1ドットのスキマの有無を繰り返す。 // 前の canvas とのかぶりチェック let kabu = false; for( let j = 0; j < i; j ++ ) { const figure0 = this.figures[ j ]; //check. if( figure0 == this.camF ) continue; if( areTheyKaburi( figure0.cc.rect, figure.rect ) ) { kabu = true; figure.cc = figure0.cc; const minX = Math.min( figure.rect[ 0 ], figure0.cc.rect[ 0 ] ); const minY = Math.min( figure.rect[ 1 ], figure0.cc.rect[ 1 ] ); const maxX = Math.max( figure.rect[ 0 ] + figure.rect[ 2 ], figure0.cc.rect[ 0 ] + figure0.cc.rect[ 2 ] ); const maxY = Math.max( figure.rect[ 1 ] + figure.rect[ 3 ], figure0.cc.rect[ 1 ] + figure0.cc.rect[ 3 ] ); figure.cc.rect[ 0 ] = minX; figure.cc.rect[ 1 ] = minY; figure.cc.rect[ 2 ] = maxX - minX; figure.cc.rect[ 3 ] = maxY - minY; figure.cc.canvas.style.left = scrollX + figure.cc.rect[ 0 ] + "px"; figure.cc.canvas.style.top = scrollY + figure.cc.rect[ 1 ] + "px"; figure.cc.canvas.width = figure.cc.rect[ 2 ]; figure.cc.canvas.height = figure.cc.rect[ 3 ]; } // canvas.width, canvas.height への代入は、 // 同値だとしても代入を行った時点で // 画面消去される。 // だから以降に clearRect() は無い。 } // for j if( kabu ) { figure.defaultCc.canvas.style.display = "none"; } else { figure.defaultCc.canvas.style.display = "block"; figure.cc = figure.defaultCc; figure.cc.rect = figure.rect; figure.cc.canvas.style.left = scrollX + figure.rect[ 0 ] + "px"; figure.cc.canvas.style.top = scrollY + figure.rect[ 1 ] + "px"; figure.cc.canvas.width = figure.rect[ 2 ]; figure.cc.canvas.height = figure.rect[ 3 ]; } //check. 右端、下端からはみ出したとき canvas サイズを小さくしてはみだしを避ける if( figure.cc.rect[ 0 ] + figure.cc.rect[ 2 ] > visualViewport.width ) { // figure.cc.rect[ 2 ] や figure.rect[ 2 ] の更新は省略 figure.cc.canvas.width = innerWidth - clientX; // innerWidth, innerHeight じゃないと、スクロールバーの分のスキマが出るときがある。 } if( figure.cc.rect[ 1 ] + figure.cc.rect[ 3 ] > visualViewport.height ) { figure.cc.canvas.height = innerHeight - clientY; } for( const model of figure.models ) { // ● //--- ポリゴン作成 for( let i = 0; i < model.mens.length; i ++ ) { const men = model.mens[ i ]; // 面 const polygon1 = [ model, // 0 : model true, // 1 : ISNOTPATHS new Array(), // 2 : tenCs2 0, // 3 : avgZ figure, // 4 : figure ] for( const tenIndex of men ) { const tenC2 = [ model.tenCss2[ MODEL ][ tenIndex ][ X ], model.tenCss2[ MODEL ][ tenIndex ][ Y ], model.tenCss2[ MODEL ][ tenIndex ][ Z ], model.tenCss2[ MODEL ][ tenIndex ][ H ] - ( figure.cc.rect[ 0 ] - this.windowOriginLeft ), // ● model.tenCss2[ MODEL ][ tenIndex ][ V ] + ( figure.cc.rect[ 1 ] - this.windowOriginTop ), // ● ] polygon1[ TENCS2 ].push( tenC2 ); polygon1[ AVGZ ] += tenC2[ Z ]; } polygon1[ AVGZ ] /= men.length; const polygon1Visibility = isPolygonVisible( polygon1[ TENCS2 ] ); if( ! model.hiddenSurfaceRemoval || polygon1Visibility ) polygons.push( polygon1 ); //debug. 面の点番号を表示するときに使用 polygon1.men = men; // 面にヒモづけられたパス if( model.pathz[ i ] ) { // i は面番号であり、pathz のキーにその面番号があれば //--- ●●●paths2 make args // paths について、関数呼び出しの場合、args を準備する。 for( const path of model.pathz[ i ] ) { const polygon2 = [ path, // 0 : paths ( pathz[ i ] は配列である ) false, // 1 : ISNOTPATHS new Array(), // 2 : tenCs2 0, // 3 : avgZ figure, // 4 : figure ] let cnt = 0; for( const drawingInst of path.drawingInsts ) { //check. if( ! drawingInst.isFunc ) continue; drawingInst.args.length = 0; for( let n = 0; n < drawingInst.xyArgsToTenIndex.length; n ++ ) { const tenIndex = drawingInst.xyArgsToTenIndex[ n ]; drawingInst.args[ n * 2 ] = model.tenCss2[ MODEL ][ tenIndex ][ H ] - figure.minH; drawingInst.args[ n * 2 + 1 ] = -model.tenCss2[ MODEL ][ tenIndex ][ V ] + figure.maxV; polygon2[ AVGZ ] += model.tenCss2[ MODEL ][ tenIndex ][ Z ]; cnt ++; } } polygon2[ AVGZ ] /= cnt; //check. 面が見えているとき、パスは面よりも手前に来るはずだ if( polygon1Visibility && polygon1[ AVGZ ] < polygon2[ AVGZ ] ) { polygon2[ AVGZ ] = polygon1[ AVGZ ]; } else if( ! polygon1Visibility && polygon1[ AVGZ ] > polygon2[ AVGZ ] ) { // 見えていないときにパスのほうが手前に来るなら、AVGZ値を交換(=描画順を交換) const tmp = polygon1[ AVGZ ]; polygon1[ AVGZ ] = polygon2[ AVGZ ]; polygon2[ AVGZ ] = polygon1[ AVGZ ]; } polygons.push( polygon2 ); } // for path pathz[i] } // if } // for i mens } // for model models } // for figure figures polygons.sort( function( a, b ) { if( a[ AVGZ ] < b[ AVGZ ] ) return 1; if( a[ AVGZ ] > b[ AVGZ ] ) return -1; return 0; } ); //--- 描画開始 // 軸 if( 0 ) { line( 0, - cc.canvas.height / 2, 0, cc.canvas.height / 2, "cyan" ); // tate line( - cc.canvas.width / 2, 0, cc.canvas.width / 2, 0, "cyan" ); // yoko } for( const polygon of polygons ) { const cc = polygon[ FIGURE ].cc; if( polygon[ ISNOTPATHS ] ) { cc.beginPath(); for( let i = 0; i < polygon[ TENCS2 ].length; i ++ ) { const tenC2 = polygon[ TENCS2 ][ i ]; if( i ) { cc.lineTo( tenC2[ H ], -tenC2[ V ] ); } else { cc.moveTo( tenC2[ H ], -tenC2[ V ] ); } } cc.closePath(); cc.fillStyle = polygon[ MODEL ].color; cc.fill() if( polygon[ FIGURE ] && polygon[ MODEL ] == polygon[ FIGURE ].baseModel ) cc.strokeStyle = "red"; else cc.strokeStyle = "rgba(0,0,0,.25)"; cc.stroke(); //debug. 点番号を表示 if( polygon[ MODEL ].debugTenNumDisp ) { for( let i = 0; i < polygon[ TENCS2 ].length; i ++ ) { const tenC2 = polygon[ TENCS2 ][ i ]; cc.fillStyle = "white"; cc.fillRect( tenC2[ H ], -tenC2[ V ], 14, -12 ); cc.fillStyle = "black"; cc.fillText( polygon.men[ i ], tenC2[ H ] + 1, -tenC2[ V ] - 2 ); } } } else { //--- ●●●paths3 描画 for( const drawingInst of polygon[ PATHS ].drawingInsts ) { if( drawingInst.isFunc ) cc[ drawingInst.name ]( ...drawingInst.args ); else cc[ drawingInst.name ] = drawingInst.args[ 0 ]; } } } // for polygon polygons } // (App).draw()


答えは、必ずしも一つではありませんが、一例として、

『この draw() 関数(メソッド)の前に初期設定として、visualViewportWidth という名前で visualViewport.width の値を事前に保存しておき、draw() 関数(メソッド)の中でその visualViewportWidth を見る』

です。

また、ウィンドウがリサイズされるとウィンドウの内部幅は変わるので、onresize でその値(visualViewportWidth)を更新するようにします。

つまり、visualViewport.width と height は、draw() の中で参照する必要はまったくなかったということです。

この改善により、draw() の中では単純な変数を見るだけとなるので早くなるはずです。


プロファイラによって速度的な問題点を浮き彫りにして、プログラマーが解決を行い、高速化を図ることができます。


3. 「プロファイラ」の使い方手順

ではプロファイラの使い方を見ていきましょう。

図は私が作っている人型モデルのプログラムで、2つのヌンチャク型モデルを表示しているところです。

ヌンチャクというのは別名、三節棍と呼ばれるもので、中国の古い武器ですね。四節棍というものもあるそうです。

右クリックして「調査」を選びます。


「開発者ツール」という画面が表示されます。

上部の真ん中付近の「パフォーマンス」タブをクリックします。

すると図のような画面になります。


ちょっと、一番下の「設定を編集...」というリンクをクリックして設定を見ることにします。


するとブラウザのほうで「プロファイラーの設定」という画面が表示されます。

ここに、測定を粗くするか細かくするかという設定があります。

「粗い測定」だと、「一瞬で終わる関数」を取りこぼすことがあります。

「細かい測定」にすれば多くの関数の実行を検知することができます。ただし、細かい分、処理が重くなります。

画面を下にスクロールして「バッファー設定」の「サンプリング間隔:」を小さい値にすれば「細かい測定」になります。

最初の内は設定を変更しないでそのままで構いませんが、測定後に必要な情報が入っていないときはこの設定を小さくするようにします。これは覚えておきましょう。

画面を×を押して閉じます。


目的のプログラムを動かします。

「この辺かな~」というところで、「開発者ツール」>「パフォーマンス」の画面で、画面中央の青い「記録を開始」ボタンを押します。


するとボタンが変わります。

この状態で測定中です。


30秒くらい時間を見てから図の「記録をキャプチャ」というボタンを押すと、測定を終了し、測定した記録のキャプチャ獲得する、取る、とらえる、つまり集計)を開始します。

CPU が core i5 8500T(6コア)のパソコンで、

上記の設定のサンプリング間隔: 0.5 ms(やや細かい)にして、

10 秒間測定すると、

キャプチャにかかる時間は 1 分でした。


結構待ちます。設定 0.5 ms で 10 秒測定というのは、

10 秒 × 1,000 ms = 10,000 ms

10,000 ms ÷ 0.5 ms = 100,000/5 = 20,000 回サンプリング

ということで、2 万個のレコードを集計(分析)しているなら 1 分かかるのも何となく分かります。


キャプチャ(集計)が終わると、プロファイラの画面が表示されます。

最初に大きく示した画面です。

グラフを見ながら、

『get VisualViewport.width が無駄なんじゃないか??』

…のようにプログラムを遅くしている原因や、高速化できる部分を探るわけです。

プロファイラがないとこの画面は見ることができません。

(実際は、キャプチャが終わるとこの画面ではなく、「Call Tree」というタブがまず最初に表示されます。補足をご覧ください)


積み上げられたグラフのうち、「draw()」のグラフにマウスを当てると、図のようにプロファイリングらしい画面が表示されます。

この表示の左上を見ると、「354 ms」と表示されています。

draw() 関数を実行するのに(測定処理も含めて)「354 ms」かかっているということです。

% 表示は、測定した期間(例では10秒)の内で「39 %」はこの draw() 関数の処理が占めている、という意味です。

つまり、プログラム全体の動きの内、40 % くらいは描画に費やしているということです。

VisualViewport.width の参照を改善することで draw() の処理時間が短くなれば良いわけです。


改善前は、図のように draw() の中で、39% を占めていましたが、改善を行って、、


このように、単なる変数の参照に変えたためか、「get VisualViewport.width」がそっくり消えました。


draw() のプロファイル画面では、

354 ms → 214 ms

のように 140 ms 短縮されました。

(測定ごとに多少のばらつきはあります)


…以上のように、プロファイラを使うと、無駄な処理を見つけることができ、効率化のきっかけとなります。

しかし、Firefox Profiler は公式の日本語マニュアルが無いし、使って解説している日本人も少ないので、習得がちょっと難しいです。

でも基本の操作は以上の通りで、基本だけでも知っていれば使い始められるのではと思います。

ゲームとか作って動きが悪いなーという人は、プロファイラを使うと良いと思います。

4. 使い方の補足

以下は補足です。使い方が難しいですから補足しないと…


「記録をキャプチャ」(集計)ボタンを押してしばらくしてから最初に表示される画面です。

この画面で、下部の「Flame Graph」というタブをクリックすると、、


このような画面になります。

タブのすぐ下の

◎All frames、○JavaScript、○Native

で、JavaScript を選ぶと、、


測定結果の内、JavaScript のレコードだけを集計したグラフに変わります。

でも、測定したとき複数のページを開いていたので、関係のないページのグラフが表示されています。

画面上部には複数の項目が並んでおり、この中から目的のページをクリックします。

項目はページ名ではなく、サーバーのドメイン名になっています。

(計測対象はプロセス単位であり、ブラウザは通常1ページ1プロセスではなく、複数ページ1プロセスで動いています。なのでこの辺はしかたありません)

ドメイン名の次に Memory や CanvasRenderer などの「測定値 種別」が並んでいます。

今回のページは「ローカルマシン」で動かしていたので、「127.0.0.1」というドメイン名(アドレス)です。

クリックすると、、


これで目的の画面になりました。

(まぁ、難しいですね。)

改めて測定したものですが、ちゃんと、

requestAnimationFrame を表す関数と、

そのコールバックに指定した frame() 関数、

draw() 関数、があります。

3D モデルの親子関係を処理してるスカイツリーも見えます。


ドメイン名が並ぶ部分の上のほうにはスクリーンショットが並んでいます。

マウスでポイントすると拡大表示されます。

公式ドキュメントによれば、このスクリーンショット表示は単なる「参考」だそうです。

測定期間中の、「画面の変化(スクリーンショット)」と、「画面上半分のグラフ(CPU使用率やメモリ使用率など)」は時間軸で対応しています。

”この画面のときにどれだけ CPU を使っていたのか” という対応関係が分かります。

画面下半分の複数のタブのうち、「Stack Chart」タブと「Marker Chart」タブのみ、画面上半分の時間軸と対応させて見ることができます。

「Call Tree」タブは樹形図的な画面で時間軸との対応はありません。

「Flame Graph」タブは測定期間中に占める、各処理の時間の割合を幅で示して並べているだけであり、時間軸との対応はありません。

「Marker Table」は表ですね。これも時間軸との対応はありません。


以上の詳細は、このプロファイラの公式ドキュメント「Getting Started」に、英文ですが、書かれています。

Firefox なら、右上の≡マークから「ページを翻訳...」で日本語にできます。うまくいっていない翻訳ですが、なんとか読めます。
測定開始のタイミングについて

「Hello World !」のような一発で終わるプログラムは、下図のように、起動直後一瞬で終了します。


それに対し、今回の 3DCG 人体モデルのようにアニメーションするプログラムは起動後、止まりません。


「Hello World !」のようなすぐに終わるプログラムの場合、プログラム起動後にプロファイラを起動しても測定はされません。


一方、アニメーションするプログラムの場合は終了することなく処理を繰り返しているのでいつでも測定できます。


「Hello World !」のような一発で終わるプログラムで測定を行うためには、

  • プロファイラを起動してから、プログラムを実行
  • プログラムを実行してキー待ちにして、プロファイラを起動してから、キーを入力し、処理を実行

のどちらかで行う必要があります。

下図は「キー入力し、処理を実行」の図です。

以上のように、測定するためには、プログラムが動いている状態でなければならないので、うまく測定する必要があります。

5. 5つのタブを説明

本気でプロファイラを使ってみようという方は以下の説明も読んでもらうと良いと思います。

どうしてここまで詳しく説明するのかと言うと、他のニュースサイトの「Firefox Profiler」の記事を読むと、「詳細は英文の公式マニュアルを読んでください」で終わっていて、どうも参考(実用)にならないからです。

ここではプロファイラを使いこなすための、下図の5つのタブについて説明します。

1. Call Tree タブ

英文公式サイトこれの翻訳:

  1. Call Tree パネルは、サンプルデータの総合的なビューです。
    スタックサンプルと Call Tree に関するページでは、サンプルデータから Call Tree 構造がどのように計算されるかを詳しく説明しています。
  2. 簡単に言うと、スタック内の共通の祖先をマージすることで、プログラムのどの部分がより頻繁に実行されているかを特定できます。
  3. フィルタリングガイドでは、このビューの操作方法(検索、JavaScriptへのフィルタリング、変換、反転 (ボトムアップツリーとも呼ばれます) )に関する役立つ情報が提供されています。
  4. 最後に、サイドバーには、選択した関数内で実行されているコードのカテゴリの内訳が表示されます。

ここで言っている「スタック」というのは、測定データの内、メモリに関する値、CPU 使用率、機械語コードの使用率、JavaScript コードの使用記録に関する値など、「種類別に振り分けたデータ」のことを言っています。

「共通の祖先をマージ」とは、たとえば、測定データAが使用率 20 %、測定データBが使用率 5 %で、それらが同じ関数から呼ばれているなら、その関数の使用率は 25 %である、という意味だと思います。共通の祖先をマージすることで「その関数は 25 %の頻度で実行されている」というのが分かるということです。

「サイドバーのカテゴリの内訳」とは、

▼説明のために Excel で計算

たとえば、frame という関数は全測定時間の 51 % を占めていて、frame 関数の処理を構成する要素としては、JavaScript や DOM 処理、メモリのごみ処理(GC/CC)などがあり、それらは 51 % を 100 % としたとき、それぞれ 32 % とか 0.9 % を占めている、という意味です。

またその中で、この測定のために要した時間は Profiler の項目が示すとおり 17 % です。

全 204 ms のうちの 17 % なので、204 ms × 0.17 = 34.68 ms ということです。

なので、frame という関数は実際は 204 ms - 34.68 ms = 169.32 ms かかっているということになります。


…そういう情報を知って、何をしようと言うのか…

正直分かりませんが、orz、たとえば DOM が 80% になっていたら、その関数は「DOM 操作をいっぱいやっている」というのが分かります。。

名探偵コナンみたいに、細かい情報に手が届くと、意外な真実が見えてくる…ということだと思います。orz

(orz は象形文字みたいなもので、四つん這いになってうなだれている人を表しています)


この画面でツリー状の項目の一つを選択すると、画面上部のグラフ盛り上がりで色が濃くハイライトされます。

CPU 使用率の盛り上がりを「構成する部分をハイライトしている」ので、その盛り上がりを崩したいなら、その処理を外せばよいという理屈になります。

2. Flame Graph タブ

英文公式サイトこれの翻訳:

  1. Flame Graph は、全く同じ Call Tree 構造をより視覚的に表示します。
  2. 長方形が大きいほど、実行時間が長いことを意味します。
  3. 上部の長方形は、セルフ実行時間に影響するスタック、つまりプログラムが実際に時間を費やしているコードです。
  4. 順序は常に同じなので、異なる範囲選択間だけでなく、異なるプロファイル間でも比較しやすくなります。
  5. 視覚的にわかりやすいため、Call Tree よりも Flame Graph を好むユーザーもいるでしょう。

前のタブの「Call Tree」をそのまま視覚化したものだ、と説明されています。

「上部の長方形」とはその関数を内部的に構成する処理のことを言っていて、関数の実行時間はその内部の処理の時間の合計だと言っています。

このタブでも長方形(項目)をクリックすると、画面上部のグラフ盛り上がりで色が濃くハイライトされます。

感覚的に分かりやすいということですね。

3. Stack Chart タブ

英文公式サイトこれの翻訳:

  1. Stack Chart パネルにもサンプルデータが表示されますが、これはサマリー(要約)ではなく、タイムラインに沿った時系列ビューです。
  2. 上のスクリーンショットでわかるように、同じカテゴリが同じタイムスタンプで表示されます。そのため、以前のパネルとは異なる視点で同じデータを表示できます。
  3. ただし、注意が必要です。このパネルでは関数呼び出しのシーケンスを再構築しようとしますが、サンプルデータの性質上、短いイベントが欠落する可能性があるため、短い呼び出しのシーケンスであるはずの呼び出しが長い呼び出しとして表示される場合があります。

前のタブの Flame Graph が要約だったのに対して、こちらは時系列に沿ったグラフだと違いを説明しています。

同じカテゴリは同じ色で色分けされていて、それらが集まって同じ時間に表示されている、と説明されています。

しかし、設定でサンプリング間隔が長く設定されていると、取りこぼしが発生するので、取りこぼしにより欠落した分 A の場所(時間)は、欠落しなかった分 B で埋められ、その結果その欠落しなかった分 B が通常よりも長く表示されてしまう、と言っています。


グラフの部分をクリックして選択すると、それは画面 上半分の時系列に沿ったグラフにきちんと対応します。

▼ RefreshDriver tick というグラフをクリック。上部の 10 個がハイライト。

▼ その構成である Update the rendering Animat... というグラフをクリック。上部の 3 個がハイライト。

▼ となりの構成である Update the rendering Paint というグラフをクリック。上部の 7 個がハイライト。

Flame Graph タブと比べると、「時間の流れとしてはどうなのか」という視点でその項目の処理の重さを客観的に見られる、、というのかなぁ。

4. Marker Chart タブ

英文公式サイトこれの翻訳:

  1. Marker Chart は、プロファイリングセッション中に発生したすべてのマーカーを時系列で視覚的に表示します。マーカーデータはソースコードインストルメンテーション(C++、Rust、JavaScript、Java)から取得されます。
  2. マーカーにマウスオーバーすると、そのデータを確認できます。例えば、CSSアニメーションマーカーの場合、アニメーションが実行される要素が表示されます。
  3. マーカーを右クリックするとコンテキストメニューが表示され、データのコピーなど、これらの情報をさらに操作できます。
  4. Web開発者は、performance.markperformance.measure を使用して独自のマーカーを追加できます。Gecko開発者は、この包括的なドキュメントを参照して新しいマーカーを追加できます。
  5. マーカーは、コンマ区切りの検索語句リストでフィルタリングできます。
    各検索語句は、
    • 一致する部分文字列、
    • より具体的に一致する [key]:[substring]、
    • 一致するものをすべて除外する -[key]:[substring]
    のいずれかで指定できます。
    (-[substring] は機能しません。否定一致には -[key]: が必要です。)
    有効なキー key 値は、
    name、cat(マーカーカテゴリの場合)
    type(ペイロードオブジェクトを持つマーカーの場合)
    およびマーカーのスキーマで宣言された任意のマーカーペイロードフィールドキーです。
  6. 例:DOM,cat:GC,-name:CSS は、
    カテゴリ、名前、タイプ、または任意のフィールドに DOM が含まれるもの、およびカテゴリに「GC」が含まれるものに一致しますが、名前に「CSS」が含まれるマーカーは除外されます。

1行目の、「ソースコードインストルメンテーション」のインストルメンテーションとは、英単語としては、「Instrumentation楽器法、管弦楽法、器具使用、器具類」という意味ですが、IT 用語としては、「遠隔より管理するための技術」という意味のようです。

つまり、「ソースコードインストルメンテーション」とは、「ソースコードの中において、プロファイラに対して指示を与えるしくみ」ということのようです。

3DCG 人体モデルのプログラムの中で、performance.mark( マーカー名 ) で「マーカー」を置き、

performance.measure( name, 開始マーカー名, 終了マーカー名 ) で、それらのマーカーの範囲に名前を付けます。

測定したい開始部分と終了部分にマーカーを置き、その後で範囲に名前を付けます。

それで同様に測定すると、、

このように Marker Chart のなかで UserTiming として測定してくれます。

ヌンチャク2つ分のポリゴン作成で、22.8us かかっていることが分かりました。

これでポリゴン作成部分について集中して高速化の結果が見られるようになったわけです。

この範囲を右クリックすると、

Set selection from marker's duration マーカーの継続時間から選択範囲を設定
Start selection at marker's start マーカーの開始位置から選択範囲を開始
Start selection at marker's end マーカーの終了位置から選択範囲を開始
End selection at marker's start マーカーの開始位置から選択範囲を終了
End selection at marker's end マーカーの終了位置から選択範囲を終了
Copy description 説明をコピー
Copy page URL ページURLをコピー
Copy as JSON JSONとしてコピー

たとえば、一番上の「マーカーの継続時間から選択範囲を設定」を行うと、

画面下部はそのマーカー範囲を全開にして表示して、

画面上部はその範囲のみハイライトし、範囲外はグレーアウトされます。

ハイライト中の虫眼鏡アイコンをクリックすると画面上部もその範囲で全開(拡大表示)します。

その他:

  • 画像の赤く囲った部分は「パンくずリスト」になっていて、これまで拡大してきた拡大率が並んでいます。
    それぞれクリックすれば拡大を解除します。
  • パンくずリストの右側の秒数はその範囲が示す時間です。
  • グレーアウトの状態はグレーの部分をクリックすると解除されます。
  • 右上の Filter Markers: の欄にキーワードを入力すると、そのキーワードに該当するマーカーだけがグラフになります。
    たとえば、
    • gc と入力すると、カテゴリ、名前、タイプ、または任意のフィールドなど、どこかしらに gc と書かれているものだけを表示します。
    • -name:GC とすれば名前に GC が入っているものを除外します。
    • -cat:javascript とすればカテゴリ名に javascript が入っているものを除外します。
    • cat:javascript とすればカテゴリ名に javascript が入っているものだけを表示します。
5. Marker Table タブ

英文公式サイトこれの翻訳:

  1. このパネルは、Marker Chart と同じデータを表形式で表示します。
    検索機能により、複数のマーカーのペイロード情報を一度に表示できるため、より高速に表示できるという利点があります。
  2. フィルタリングは Marker Chart と同様に機能します。

ペイロードというのは「有効荷重、積み荷」という意味のようです。

各マーカーには、名前、カテゴリ、共通のオプション情報(タイミング、バックトレースなど)、および特定のタイプのオプションペイロード(そのタイプに関連する任意のデータを含む)が含まれます。

というような記載があるので、おそらくペイロード=情報だと思います。

ペイロードオブジェクトと言ったら、「情報を積んでいるオブジェクト、そのためのオブジェクト」という意味合いだと思います。

6. 2つの測定を比較する

比較の機能はあるにはあるのですが、最終的な画面でうまく比較できない感じがしています。

というのも、同じ処理でも JavaScript は臨機応変に処理を変えているみたいで、比較しづらいという結果になっているのかな、、と思います。

私のやり方が何か間違っているのかもしれませんが…

結果はあまり良くない、とあらかじめ言っておきますが、この機能を試す手順を以下に示します。


6-1. 比較のための準備

6-2. 改善前のページについて準備する

6-3. 改善後のページについて準備する

6-4. 比較を行う

6-1. 比較のための準備

F12 キーを押すなどして「開発ツール」>「パフォーマンス」タブを表示します。


そしてボタンのすぐ下のリンク profiler.firefox.com をクリックします。


すると「Firefox Profiler」の公式サイトが表示されます。


スクロールして一番下の、

You can also compare recordings. Open the comparing interface.

のリンクをクリックします。

これは「記録を比較することもできます。比較インターフェースを開いてください。」と書いてあります。


すると図ようなページが表示されます。

翻訳は以下の通りです。

Firefox Profiler
Firefox のパフォーマンス分析用 Web アプリ

比較したいプロファイルの URL を入力してください。
このツールは、各プロファイルについて、選択したトラックと範囲からデータを抽出し、同じビューに表示して簡単に比較できるようにします。

プロファイル 1: http://
プロファイル 2: http://

[ プロファイルを取得 ]

この画面に、

改善前のページの測定プロファイルの URL

と、

改善後のページの測定プロファイルの URL

を記入することになります。

今はまだ記入しません。


測定の前に、設定の画面を開いて、

  • サンプリング間隔: 0.2 ms
  • スクリーンショット:チェックを外す図)

という設定にした方がいいかもしれません。

関数の検知を取りこぼしてしまうと、比較しづらくなりそうなので、サンプリング間隔は細かくします。

また、慣れるとスクリーンショットはあまり見ないので、オフにして、CPU の力を測定のほうに注力させます。


6-2. 改善前のページについて準備する

改善前のページの測定を行います。

測定結果が表示されたら、

まず「Stack Chart」タブを表示します。


ツララのようにグラフが並んでいますが、この中で、下方向に十分に伸びているグラフを探します。

(illustAC)

このグラフをとらえるように、画面の上半分のグラフ上でドラッグして範囲選択します。


すると、上半分では範囲選択され、下半分ではその範囲が拡大されて表示されます。


次に上半分の範囲選択の中の虫眼鏡のアイコンをクリックします。


すると、上半分のほうも範囲が拡大されて表示されます。

下半分のツララに合わせて、上半分で盛り上がりがあります。


この部分を範囲選択します。


すると、下半分の方でグラフが大きく見えます。


次に「Marker Chart」タブをクリックします。


すると、このようになります。

この中で、比較するのにちょうど良さそうな処理を選びます。

ここでは requestAnimationFrame callb... を選ぶことにして…


… requestAnimationFrame callb... の行にある青いグラフを右クリックして、表示されるメニューの一番上の Set selection from marker's duration を選びます。


すると、下半分の画面が、

requestAnimationFrame callb... 青いグラフをめいっぱい表示するように拡大表示されます。


次に上半分の範囲選択の中の虫眼鏡のアイコンをクリックします。


中途結果として最終的にこのような画面が得られます。

このブラウザ画面はこの状態でひとまず置いておきます。


次に、改善後のページの測定を行います。

繰り返しになりますが同じことをやります。


6-3. 改善後のページについて準備する

改善後のページの測定を行います。

まず「Stack Chart」タブを表示します。


ツララのようにグラフが並んでいますが、この中で、下方向に十分に伸びているグラフを探します。


このグラフをとらえるように、画面の上半分のグラフ上でドラッグして範囲選択します。


すると、上半分では範囲選択され、下半分ではその範囲が拡大されて表示されます。


次に上半分の範囲選択の中の虫眼鏡のアイコンをクリックします。


すると、上半分のほうも範囲が拡大されて表示されます。

下半分のツララに合わせて、上半分で盛り上がりがあります。


この部分を範囲選択します。


すると、下半分の方でグラフが大きく見えます。


次に Marker Chart ボタンを押します。


すると、このようになります。

この中で、改善前ですでに選んだ処理の行を見ます。

改善前では requestAnimationFrame callb... を選んでいました…


… requestAnimationFrame callb... の行にある青いグラフを右クリックして、表示されるメニューの一番上の Set selection from marker's duration を選びます。


すると、下半分の画面が、

requestAnimationFrame callb... 青いグラフをめいっぱい表示するように拡大表示されます。


次に上半分の範囲選択の中の虫眼鏡のアイコンをクリックします。


中途結果として最終的にこのような画面が得られます。

このブラウザ画面はこの状態でひとまず置いておきます。


改善前と改善後とで準備が整ったので、次の作業に移ります。


6-4. 比較を行う

改善前のページを表示します。

そして、右上の「Upload Local Profile」をクリックすると図のような小さな画面が表示されます。

翻訳すると、

パフォーマンス プロファイルを共有

プロファイルをアップロードし、リンクを知っているすべての人がアクセスできるようにします。
デフォルトでは、個人データは削除されます。

より詳細にするための追加データを含める
□ 非表示のスレッドを含める
□ 非表示の期間を含める
□ スクリーンショットを含める
□ リソースの URL とパスを含める
□ 拡張機能情報を含める
[ Download(800KB) ] [ Upload ]

と書かれています。

今回はどれもチェックしないで説明を進めます。


青い Upload ボタンを押しましょう。


すると、右上に URL が表示されます。

これをコピーして先ほどの Profile 1: と Profile 2: の記入欄が縦に並んだ画面

を表示させ、画面の Profile 1: に貼り付けましょう。


改善後についても同様に、右上の「Upload Local Profile」をクリックし、「Upload」を行い、表示された URL をコピーし、この Profile 2: に貼り付けします。


そして、Profile 1: と Profile 2: の入力欄の下の青い Retrieve Profiles ボタンを押します。


これで、やっとで比較の画面が表示されます。

表示させたのは良いのですが、先にお伝えしたように、この画面の活用方法がよく分からないんですよね。

  • 表示されたページのタイトルは「Compare Profiles – Firefox Profiler」となっています。
  • 画面上半分の項目は3つあり、
    • Diff between 1 and 2、
    • Profile 1: Isolated Web C...
    • Profile 2: Isolated Web C...
    とあります。(順不同です)
  • 時間軸は、1.4 ms あります。
    Profile 2 のほうは途中でグレーになっています。
    Profile 2 は改善されて速度アップし、requestAnimationFrame callb... が早く終わっているので、そこまでしか測定データが無いんですね。
  • 上半分の項目の「Diff between 1 and 2」は、タブが「Call Tree」しかありません。
    Call Tree のツリー表示では、
    • frame のフォルダが2つあり、
    • 1つ目の frame フォルダは、
      左端の % 表示がマイナスになっています。
      改善前なので、get VisualViewport.width があります。
    • 2つ目の frame フォルダは、一番下で閉じられた状態です。
      クリックして * キーを押すとすべて展開されます。
      左端の % 表示はマイナスではありません。
      改善後なので、get VisualViewport.width がありません。
    • 測定で取りこぼしがあったのか、一部の項目があったりなかったりしているように見えます。

    この項目「Diff between 1 and 2」は、どのように見て、どう活用するのか正直わかりませんが、まぁ、このように表示できます…という話です。


… そのほか、frame や draw などの関数のグラフに注目して、かかっている時間を見ると、

frame 1.4 ms → 0.79 ms

draw 1.2 ms → 0.79 ms

という時間短縮のかたちで改善が見られます。

…でも正直、ちょっとうまく比較できない感じがしていて、私のやり方が悪いのか、この比較機能は謎の状態です。


改善前と改善後の測定結果をウィンドウで左右に並べて比べたほうが早いかも。


▼requestAnimationFrame の処理内容の違いを大まかに確認できる。


▼ツリーを並べて、各項目の処理時間を右のサイドバーに表示させて比べることができる。


▼改善前(左)と改善後(右)のツララを見ると改善後はツララが少なく見える。(改善されてる)


▼フィルタで requestAnimationFrame call.. のみ表示。ざっと一覧し右のほうがかかる秒数が少ないのがわかる。


▼frame() 関数の前後に置いたマーカーで一覧表示。

プロファイラを使って、秒数を手軽に見ることができるのでオススメ。

(用意された比較機能はまた今度使えるようになったら使えば良いでしょう)

(訪問者のどんなニーズと この記事がつながるか)

  • プロファイラの使い方
  • プログラミングの話
  • 日記を読みたい

2025年8月24日

生活 花3

右の咲かないデージーの隣に友達を買ってきて置きました。

20年前、2匹いた猫たちの片方が死んでしまい、1匹になったとき、さびしがるので何度か友達を検討しましたが、新しい友達を前の友達のように気に入るならいいけど、もし気に入らなかったらマイナスになるなと思い、用意することはできませんでした。

お店の小さなペラペラ鉢からいきなりデカ鉢へ移したので、しばらくのあいだは発育し放題です。

お店での手入れが不十分だった?せいか、葉っぱが一部黄色いですけど、健康になることを願います。

切り花の方は8/8にお店で買い、今回の撮影が8/24。

16日間。2週間以上経っています。

一部枯れているけど、いまだに美しさを保っている。

土に植えた花でさえ、こんなに長期間もたないのではと思います。

  • おいしい水をペットボトルから出さずにペットボトルごと使い、(雑菌極少)
  • 「切り花長持ち」の液体を使い、(雑菌撲滅)
  • 花瓶上部にはアルミ箔でフタをして、(雑菌対策)
  • 日のあたりが短い涼しい窓際に置いた

…のが良かったのだろうか?と思います。

でももう限界で、花瓶から取り出し、枯れさせるほうがいいのか?と思っています。

同じ条件でも次はないかな(今回は単なるラッキー)と思います。

今回、水が無くなっているのに気づき、入れたのは水道水で長持ちの液体も無いので、一気に枯れるかなと思います。

おいしい水も液体も用意したくても無いんだな…

殺伐とした部屋に、花があるとまるで違う世界になる、と思いました。ありがたいと思います。

(訪問者のどんなニーズと この記事がつながるか)

  • 花の話
  • 日記を読みたい